home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_300
/
313_01
/
search.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-04-21
|
20KB
|
1,003 lines
/* $Header: /nw2/tony/src/stevie/src/RCS/search.c,v 1.16 89/08/06 09:50:51 tony Exp $
*
* This file contains various searching-related routines. These fall into
* three groups: string searches (for /, ?, n, and N), character searches
* within a single line (for f, F, t, T, etc), and "other" kinds of searches
* like the '%' command, and 'word' searches.
*/
#include "stevie.h"
#include "regexp.h" /* Henry Spencer's (modified) reg. exp. routines */
/*
* String searches
*
* The actual searches are done using Henry Spencer's regular expression
* library.
*/
#define BEGWORD "([^a-zA-Z0-9_]|^)" /* replaces "\<" in search strings */
#define ENDWORD "([^a-zA-Z0-9_]|$)" /* likewise replaces "\>" */
#define BEGCHAR(c) (islower(c) || isupper(c) || isdigit(c) || ((c) == '_'))
bool_t begword; /* does the search include a 'begin word' match */
/*
* mapstring(s) - map special backslash sequences
*/
static char *
mapstring(s)
register char *s;
{
static char ns[80];
register char *p;
begword = FALSE;
for (p = ns; *s ;s++) {
if (*s != '\\') { /* not an escape */
*p++ = *s;
continue;
}
switch (*++s) {
case '/':
*p++ = '/';
break;
case '<':
strcpy(p, BEGWORD);
p += strlen(BEGWORD);
begword = TRUE;
break;
case '>':
strcpy(p, ENDWORD);
p += strlen(ENDWORD);
break;
default:
*p++ = '\\';
*p++ = *s;
break;
}
}
*p++ = NUL;
return ns;
}
static char *laststr = NULL;
static int lastsdir;
static LPTR *
ssearch(dir,str)
int dir; /* FORWARD or BACKWARD */
char *str;
{
LPTR *bcksearch(), *fwdsearch();
LPTR *pos;
char *old_ls = laststr;
reg_ic = P(P_IC); /* tell the regexp routines how to search */
laststr = strsave(str);
lastsdir = dir;
if (old_ls != NULL)
free(old_ls);
if (dir == BACKWARD) {
smsg("?%s", laststr);
pos = bcksearch(mapstring(laststr));
} else {
smsg("/%s", laststr);
pos = fwdsearch(mapstring(laststr));
}
/*
* This is kind of a kludge, but its needed to make
* 'beginning of word' searches land on the right place.
*/
if (pos != NULL && begword) {
if (pos->index != 0 || !BEGCHAR(pos->linep->s[0]))
pos->index += 1;
}
return pos;
}
bool_t
dosearch(dir,str)
int dir;
char *str;
{
LPTR *p;
if (str == NULL)
str = laststr;
got_int = FALSE;
if ((p = ssearch(dir,str)) == NULL) {
if (got_int)
msg("Interrupt");
else
msg("Pattern not found");
got_int = FALSE;
return FALSE;
} else {
LPTR savep;
cursupdate();
/*
* if we're backing up, we make sure the line we're on
* is on the screen.
*/
setpcmark();
*Curschar = savep = *p;
set_want_col = TRUE;
cursupdate();
return TRUE;
}
}
#define OTHERDIR(x) (((x) == FORWARD) ? BACKWARD : FORWARD)
bool_t
repsearch(flag)
int flag;
{
int dir = lastsdir;
bool_t found;
if ( laststr == NULL ) {
beep();
return FALSE;
}
found = dosearch(flag ? OTHERDIR(lastsdir) : lastsdir, laststr);
/*
* We have to save and restore 'lastsdir' because it gets munged
* by ssearch() and winds up saving the wrong direction from here
* if 'flag' is true.
*/
lastsdir = dir;
return found;
}
/*
* regerror - called by regexp routines when errors are detected.
*/
void
regerror(s)
char *s;
{
emsg(s);
}
static LPTR *
fwdsearch(str)
register char *str;
{
static LPTR infile;
register LPTR *p;
regexp *prog;
register char *s;
register int i;
if ((prog = regcomp(str)) == NULL) {
emsg("Invalid search string");
return NULL;
}
p = Curschar;
i = Curschar->index + 1;
do {
s = p->linep->s + i;
if (regexec(prog, s, i == 0)) { /* got a match */
infile.linep = p->linep;
infile.index = (int) (prog->startp[0] - p->linep->s);
free((char *)prog);
return (&infile);
}
i = 0;
if (got_int)
goto fwdfail;
} while ((p = nextline(p)) != NULL);
/*
* If wrapscan isn't set, then don't scan from the beginning
* of the file. Just return failure here.
*/
if (!P(P_WS))
goto fwdfail;
/* search from the beginning of the file to Curschar */
for (p = Filemem; p != NULL ;p = nextline(p)) {
s = p->linep->s;
if (regexec(prog, s, TRUE)) { /* got a match */
infile.linep = p->linep;
infile.index = (int) (prog->startp[0] - s);
free((char *)prog);
return (&infile);
}
if (p->linep == Curschar->linep)
break;
if (got_int)
goto fwdfail;
}
fwdfail:
free((char *)prog);
return NULL;
}
static LPTR *
bcksearch(str)
char *str;
{
static LPTR infile;
register LPTR *p = &infile;
register char *s;
register int i;
register char *match;
regexp *prog;
/* make sure str isn't empty */
if (str == NULL || *str == NUL)
return NULL;
if ((prog = regcomp(str)) == NULL) {
emsg("Invalid search string");
return NULL;
}
*p = *Curschar;
if (dec(p) == -1) { /* already at start of file? */
*p = *Fileend;
p->index = strlen(p->linep->s) - 1;
}
if (begword) /* so we don't get stuck on one match */
dec(p);
i = p->index;
do {
s = p->linep->s;
if (regexec(prog, s, TRUE)) { /* match somewhere on line */
/*
* Now, if there are multiple matches on this line,
* we have to get the last one. Or the last one
* before the cursor, if we're on that line.
*/
match = prog->startp[0];
while (regexec(prog, prog->endp[0], FALSE)) {
if ((i >= 0) && ((prog->startp[0] - s) > i))
break;
match = prog->startp[0];
}
if ((i >= 0) && ((match - s) > i)) {
i = -1;
continue;
}
infile.linep = p->linep;
infile.index = (int) (match - s);
free((char *)prog);
return (&infile);
}
i = -1;
if (got_int)
goto bckfail;
} while ((p = prevline(p)) != NULL);
/*
* If wrapscan isn't set, bag the search now
*/
if (!P(P_WS))
goto bckfail;
/* search backward from the end of the file */
p = prevline(Fileend);
do {
s = p->linep->s;
if (regexec(prog, s, TRUE)) { /* match somewhere on line */
/*
* Now, if there are multiple matches on this line,
* we have to get the last one.
*/
match = prog->startp[0];
while (regexec(prog, prog->endp[0], FALSE))
match = prog->startp[0];
infile.linep = p->linep;
infile.index = (int) (match - s);
free((char *)prog);
return (&infile);
}
if (p->linep == Curschar->linep)
break;
if (got_int)
goto bckfail;
} while ((p = prevline(p)) != NULL);
bckfail:
free((char *)prog);
return NULL;
}
/*
* dosub(lp, up, cmd)
*
* Perform a substitution from line 'lp' to line 'up' using the
* command pointed to by 'cmd' which should be of the form:
*
* /pattern/substitution/g
*
* The trailing 'g' is optional and, if present, indicates that multiple
* substitutions should be performed on each line, if applicable.
* The usual escapes are supported as described in the regexp docs.
*/
void
dosub(lp, up, cmd)
LPTR *lp, *up;
char *cmd;
{
LINE *cp;
char *pat, *sub;
regexp *prog;
int nsubs;
bool_t do_all; /* do multiple substitutions per line */
/*
* If no range was given, do the current line. If only one line
* was given, just do that one.
*/
if (lp->linep == NULL)
*up = *lp = *Curschar;
else {
if (up->linep == NULL)
*up = *lp;
}
pat = ++cmd; /* skip the initial '/' */
while (*cmd) {
if (*cmd == '\\') /* next char is quoted */
cmd += 2;
else if (*cmd == '/') { /* delimiter */
*cmd++ = NUL;
break;
} else
cmd++; /* regular character */
}
if (*pat == NUL) {
emsg("NULL pattern specified");
return;
}
sub = cmd;
do_all = FALSE;
while (*cmd) {
if (*cmd == '\\') /* next char is quoted */
cmd += 2;
else if (*cmd == '/') { /* delimiter */
do_all = (cmd[1] == 'g');
*cmd++ = NUL;
break;
} else
cmd++; /* regular character */
}
reg_ic = P(P_IC); /* set "ignore case" flag appropriately */
if ((pr